package nachos.threads;

import nachos.machine.*;

/**
 *一把锁是有2种状态的同步原语——忙或闲。有2种操作可以在锁上进行。
 *<ul>
 *	<li>获得锁：原子地等待锁的状态变为free，并且将之设置为busy
 *<li>释放锁：设置锁的状态为free，如果可能，唤醒一个正在等待的线程。
 *</ul>
 * 另外，只有获得锁的线程才能释放它。因为由于信号量的作用，API不允许你读锁的状态。
 * (因为可能你读取之后，锁的状态马上就改变了)
 * <p>
 * A <tt>Lock</tt> is a synchronization primitive that has two states,
 * <i>busy</i> and <i>free</i>. There are only two operations allowed on a
 * lock:
 * <ul>
 * <li><tt>acquire()</tt>: atomically wait until the lock is <i>free</i> and
 * then set it to <i>busy</i>.
 * <li><tt>release()</tt>: set the lock to be <i>free</i>, waking up one
 * waiting thread if possible.
 * </ul>
 *
 * <p>
 * Also, only the thread that acquired a lock may release it. As with
 * semaphores, the API does not allow you to read the lock state (because the
 * value could change immediately after you read it).
 */
public class Lock {
    /**
     * 分配一把锁，初值为free<p>
     * Allocate a new lock. The lock will initially be <i>free</i>.
     */
    public Lock() {
    }

    /**
     * 原子地获得一把锁。当前线程不应该已经获得了这把锁。
     * 如果该锁被其他线程占有，当前线程进入等待队列。
     * <p>
     * Atomically acquire this lock. The current thread must not already hold
     * this lock.
     */
    public void acquire() {
	Lib.assertTrue(!isHeldByCurrentThread());

	boolean intStatus = Machine.interrupt().disable();
	KThread thread = KThread.currentThread();

	if (lockHolder != null) {
	    waitQueue.waitForAccess(thread);
	    KThread.sleep();
	}
	else {
	    waitQueue.acquire(thread);
	    lockHolder = thread;
	}

	Lib.assertTrue(lockHolder == thread);

	Machine.interrupt().restore(intStatus);
    }

    /**
     * 原子地释放一把锁，允许其他线程得到它。如果其他线程在等待，其他线程进入ready
     * <p>
     * Atomically release this lock, allowing other threads to acquire it.
     */
    public void release() {
	Lib.assertTrue(isHeldByCurrentThread());

	boolean intStatus = Machine.interrupt().disable();

	if ((lockHolder = waitQueue.nextThread()) != null)
	    lockHolder.ready();
	
	Machine.interrupt().restore(intStatus);
    }

    /**
     *测试当前线程是否拥有锁<p>
     * Test if the current thread holds this lock.
     * @return	true if the current thread holds this lock.
     */
    public boolean isHeldByCurrentThread() {
	return (lockHolder == KThread.currentThread());
    }

    private KThread lockHolder = null;
    private ThreadQueue waitQueue =
	ThreadedKernel.scheduler.newThreadQueue(true);
}
